#include "DmMainAfx.h"

#include "xGraphic_Type.h"
#include "StretchBltImpl.h"
#include "StretchTable.h"

#include <mmintrin.h>
#include <emmintrin.h> 

extern DWORD StretchTable[(2<<8)+1];
extern const BYTE* ColorTable;

BOOL xRender::StretchBlt(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;

    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight;
    DWORD dstWidth = lpbDst->bmWidth;
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD srcy16 = 0;

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        DWORD srcx16 = 0;
        for (int x = 0; x < lpbDst->bmWidth; x++)
        {
            lpScanDst[x] = lpScanSrc[srcx16 >> 16];
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    return TRUE;
}

BOOL xRender::StretchBltSSE(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;

    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight;
    DWORD dstWidth = lpbDst->bmWidth;
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD srcy16 = 0;

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        __asm
        {
            mov esi, lpScanSrc
            mov edi, lpScanDst
            mov edx, xrIntFloat16
            mov ecx, dstWidth
            xor eax, eax
            and ecx, (not 3)
            je ENDLOOP
            lea edi, [edi+ecx*4]
            neg ecx
WRITELOOP:
            mov ebx, eax
            shr eax, 16
            movd mm0, [esi+eax*4]
            mov eax, ebx
            lea ebx, [eax+edx]
            shr ebx, 16
            punpckldq mm0, [esi+ebx*4]
            lea eax, [eax+edx*2]
            mov ebx, eax
            shr eax, 16
            movd mm1, [esi+eax*4]
            mov eax, ebx
            lea ebx, [eax+edx]
            shr ebx, 16
            punpckldq mm1, [esi+ebx*4]
            lea eax, [eax+edx*2]
            movq qword ptr [edi+ecx*4], mm0
            movq qword ptr [edi+ecx*4+8], mm1
            add ecx, 4
            jnz WRITELOOP
ENDLOOP:
            mov ebx, eax
            mov ecx, dstWidth
            and ecx, 3
            je END
            lea edi, [edi+ecx*4]
            neg ecx
BORDER:
            mov eax, ebx
            shr eax, 16
            mov eax, [esi+eax*4]
            mov [edi+ecx*4], eax
            add ebx, edx
            inc ecx
            jnz BORDER
END:
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    __asm
    {
        emms
    }
    return TRUE;
}

BOOL xRender::StretchBlt(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy;

    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;
    DWORD srcy16 = (DWORD)(lprcClipped->top * yrIntFloat16);
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD dstByteWidth = lpbDst->bmWidthBytes;

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        DWORD srcx16 = (DWORD)(lprcClipped->left * xrIntFloat16);

        for (int x = 0; x < lpbDst->bmWidth; x++)
        {
            lpScanDst[x] = lpScanSrc[srcx16 >> 16];
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += dstByteWidth;
    }

    return TRUE;
}

BOOL xRender::StretchBltSSE(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy;
    DWORD dstWidth = lpbDst->bmWidth;
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;
    DWORD srcy16 = (DWORD)((lprcClipped->top) * yrIntFloat16);
    DWORD srcx16 = (DWORD)((lprcClipped->left) * xrIntFloat16);
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD dstByteWidth = lpbDst->bmWidthBytes;

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + (srcy16 >> 16) * srcByteWidth);
        DWORD srcIndex = srcx16;
        __asm
        {
            mov esi, lpScanSrc
            mov edi, lpScanDst
            mov edx, xrIntFloat16
            mov ecx, dstWidth
            mov eax, srcIndex
            and ecx, (not 3)
            je ENDLOOP
            lea edi, [edi+ecx*4]
            neg ecx
WRITELOOP:
            mov ebx, eax
            shr eax, 16
            movd mm0, [esi+eax*4]
            mov eax, ebx
            lea ebx, [eax+edx]
            shr ebx, 16
            punpckldq mm0, [esi+ebx*4]
            lea eax, [eax+edx*2]
            mov ebx, eax
            shr eax, 16
            movd mm1, [esi+eax*4]
            mov eax, ebx
            lea ebx, [eax+edx]
            shr ebx, 16
            punpckldq mm1, [esi+ebx*4]
            lea eax, [eax+edx*2]
            movq qword ptr [edi+ecx*4], mm0
            movq qword ptr [edi+ecx*4+8], mm1
            add ecx, 4
            jnz WRITELOOP
ENDLOOP:
            mov ebx, eax
            mov ecx, dstWidth
            and ecx, 3
            je END
            lea edi, [edi+ecx*4]
            neg ecx
BORDER:
            mov eax, ebx
            shr eax, 16
            mov eax, [esi+eax*4]
            mov [edi+ecx*4], eax
            add ebx, edx
            inc ecx
            jnz BORDER
END:
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += dstByteWidth;
    }

    __asm
    {
        emms
    }

    return TRUE;
}

inline void BilinearPixel(LPDWORD c0, LPDWORD c1, DWORD u, DWORD v, LPDWORD result)
{               
    DWORD pm3 = (u * v) >> 8;
    DWORD pm2 = u - pm3;
    DWORD pm1 = v - pm3;
    DWORD pm0 = 256 - pm1- pm2 - pm3;

    DWORD c = *c0;
    DWORD BR = (c & 0x00FF00FF) * pm0;
    DWORD GA = ((c & 0xFF00FF00) >> 8) * pm0;
    c = c0[1];
    GA += ((c & 0xFF00FF00) >> 8) * pm2;
    BR += (c & 0x00FF00FF)* pm2;
    c = *c1;
    GA += ((c & 0xFF00FF00) >> 8) * pm1;
    BR += (c & 0x00FF00FF)*pm1;
    c = c1[1];
    GA += ((c & 0xFF00FF00) >> 8) * pm3;
    BR += (c & 0x00FF00FF) * pm3;

    *(DWORD*)(result)=(GA & 0xFF00FF00)|((BR & 0xFF00FF00)>>8);
}

BOOL xRender::StretchBltBilinear(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;

    DWORD xrIntFloat16 = ((lpbSrc->bmWidth - 1) << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = ((lpbSrc->bmHeight - 1) << 16) / lpbDst->bmHeight;
    DWORD dstWidth = lpbDst->bmWidth;
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD srcy16 = 0;

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        DWORD v = (srcy16 & 0xFFFF) >> 8;
        LPDWORD lpPixelSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        DWORD srcx16 = 0;

        for (int x = 0; x < lpbDst->bmWidth; x++)
        {
            LPDWORD pixel = &lpPixelSrc[srcx16 >> 16];
            BilinearPixel(pixel, (LPDWORD)((LPBYTE)(pixel) + srcByteWidth), (srcx16 & 0xFFFF) >> 8, v, &lpScanDst[x]);
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    return TRUE;
}

BOOL xRender::StretchBltBilinearMMX(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;

    DWORD xrIntFloat16 = ((lpbSrc->bmWidth - 1) << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = ((lpbSrc->bmHeight - 1) << 16) / lpbDst->bmHeight;
    DWORD dstWidth = lpbDst->bmWidth;
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD srcy16 = 0;
    __asm
    {
        pxor mm7, mm7
    }
    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        DWORD v = (srcy16 & 0xFFFF) >> 8;
        LPDWORD lpPixelSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        LPDWORD lpPixelSrcNext = (LPDWORD)((LPBYTE)(lpPixelSrc) + srcByteWidth);
        __asm
        {
            movd mm6, v
            punpcklwd mm6, mm6
            punpckldq mm6, mm6
            mov esi, lpPixelSrc
            mov ecx, lpPixelSrcNext
            xor edx, edx
            mov ebx, dstWidth
            mov edi, lpScanDst
            push ebx
            and ebx, (not 1)
            je ENDLOOP
            lea edi, [edi+ebx*4]
            neg ebx
STARTLOOP:
            mov eax, edx
            shl eax, 16
            shr eax, 24
            push ebx
            movd mm5, eax
            mov eax, edx
            shr eax, 16
            mov ebx, xrIntFloat16
            movd mm0, dword ptr [ecx+eax*4+4]
            movd mm2, dword ptr [ecx+eax*4]
            punpcklwd mm5, mm5
            movd mm1, dword ptr [esi+eax*4+4]
            movd mm3, dword ptr [esi+eax*4]
            punpckldq mm5, mm5
            punpcklbw mm0, mm7
            punpcklbw mm1, mm7
            punpcklbw mm2, mm7
            punpcklbw mm3, mm7
            psubw mm0, mm2
            psubw mm1, mm3
            psllw mm2, 8
            psllw mm3, 8
            pmullw mm0, mm5
            pmullw mm1, mm5
            paddw mm0, mm2
            paddw mm1, mm3
            psrlw mm0, 8
            psrlw mm1, 8
            lea eax, [edx+ebx]
            psubw mm0, mm1
            shl eax, 16
            psllw mm1, 8
            shr eax, 24
            pmullw mm0, mm6
            movd mm5, eax 
            paddw mm0, mm1
            lea eax, [edx+ebx]
            psrlw mm0, 8
            lea edx, [edx+ebx*2]
            shr eax, 16
            pop ebx
            packuswb mm0, mm7
            movq mm4, mm0
            movd mm0, dword ptr [ecx+eax*4+4]
            movd mm2, dword ptr [ecx+eax*4]
            punpcklwd mm5, mm5
            movd mm1, dword ptr [esi+eax*4+4]
            movd mm3, dword ptr [esi+eax*4]
            punpckldq mm5, mm5
            punpcklbw mm0, mm7
            punpcklbw mm1, mm7
            punpcklbw mm2, mm7
            punpcklbw mm3, mm7
            psubw mm0, mm2
            psubw mm1, mm3
            psllw mm2, 8
            psllw mm3,8
            pmullw mm0, mm5
            pmullw mm1, mm5
            paddw mm0, mm2
            paddw mm1, mm3
            psrlw mm0, 8
            psrlw mm1, 8
            psubw mm0, mm1
            psllw mm1, 8
            pmullw mm0, mm6
            paddw mm0, mm1
            psrlw mm0, 8
            packuswb mm0, mm7
            punpckldq mm4, mm0
            movq qword ptr [edi+ebx*4], mm4
            add ebx, 2
            jnz STARTLOOP
ENDLOOP:
            pop ebx
            and ebx, 1
            je END
            lea edi, [edi+ebx*4]
            neg ebx
            mov eax, edx
            shl eax, 16
            shr eax, 24
            movd mm5, eax
            mov eax, edx
            shr eax, 16
            movd mm2, dword ptr [ecx+eax*4]
            movd mm0, dword ptr [ecx+eax*4+4]
            punpcklwd mm5, mm5
            movd mm3, dword ptr [esi+eax*4]
            movd mm1, dword ptr [esi+eax*4+4]
            punpckldq mm5, mm5
            punpcklbw mm0, mm7
            punpcklbw mm1, mm7
            punpcklbw mm2, mm7
            punpcklbw mm3, mm7
            psubw mm0, mm2
            psubw mm1, mm3
            psllw mm2, 8
            psllw mm3, 8
            pmullw mm0, mm5
            pmullw mm1, mm5
            paddw mm0, mm2
            paddw mm1, mm3
            psrlw mm0, 8
            psrlw mm1, 8
            psubw mm0, mm1
            psllw mm1, 8
            pmullw mm0, mm6
            paddw mm0, mm1
            psrlw mm0, 8
            packuswb mm0, mm7
            movd dword ptr [edi+ebx*4], mm0
END:
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    __asm
    {
        emms
    }
    return TRUE;
}

BOOL xRender::StretchBltBilinear(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
    DWORD xrIntFloat16 = ((lpbSrc->bmWidth - 1) << 16) / lpszDst->cx;
    DWORD yrIntFloat16 = ((lpbSrc->bmHeight - 1) << 16) / lpszDst->cy;

    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;
    DWORD srcy16 = (DWORD)(lprcClipped->top * yrIntFloat16);
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD dstByteWidth = lpbDst->bmWidthBytes;

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        DWORD v = (srcy16 & 0xFFFF) >> 8;
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + (srcy16 >> 16) * srcByteWidth);
        DWORD srcx16 = (DWORD)(lprcClipped->left * xrIntFloat16);

        for (int x = 0; x < lpbDst->bmWidth; x++)
        {
            LPDWORD pixel = &lpScanSrc[srcx16 >> 16];
            BilinearPixel(pixel, (LPDWORD)((LPBYTE)(pixel) + srcByteWidth), (srcx16 & 0xFFFF) >> 8, v, & lpScanDst[x]);
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += dstByteWidth;
    }

    return TRUE;
}

BOOL xRender::StretchBltBilinearMMX(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
    DWORD xrIntFloat16 = ((lpbSrc->bmWidth - 1) << 16) / lpszDst->cx;
    DWORD yrIntFloat16 = ((lpbSrc->bmHeight - 1) << 16) / lpszDst->cy;

    DWORD dstWidth = lpbDst->bmWidth;
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;

    DWORD srcy16 = (DWORD)((lprcClipped->top) * yrIntFloat16);
    DWORD srcx16 = (DWORD)((lprcClipped->left) * xrIntFloat16);

    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD dstByteWidth = lpbDst->bmWidthBytes;

    __asm
    {
        pxor mm7, mm7
    }
    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        DWORD v = (srcy16 & 0xFFFF) >> 8;
        LPDWORD lpPixelSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        LPDWORD lpPixelSrcNext = (LPDWORD)((LPBYTE)(lpPixelSrc) + srcByteWidth);
        DWORD srcIndex = srcx16;
        __asm
        {
            movd mm6, v
            punpcklwd mm6, mm6
            punpckldq mm6, mm6
            mov esi, lpPixelSrc
            mov ecx, lpPixelSrcNext
            mov edx, srcIndex
            mov ebx, dstWidth
            mov edi, lpScanDst
            push ebx
            and ebx, (not 1)
            je ENDLOOP
            lea edi, [edi+ebx*4]
            neg ebx
STARTLOOP:
            mov eax, edx
            shl eax, 16
            shr eax, 24
            push ebx
            movd mm5, eax
            mov eax, edx
            shr eax, 16
            mov ebx, xrIntFloat16
            movd mm0, dword ptr [ecx+eax*4+4]
            movd mm2, dword ptr [ecx+eax*4]
            punpcklwd mm5, mm5
            movd mm1, dword ptr [esi+eax*4+4]
            movd mm3, dword ptr [esi+eax*4]
            punpckldq mm5, mm5
            punpcklbw mm0, mm7
            punpcklbw mm1, mm7
            punpcklbw mm2, mm7
            punpcklbw mm3, mm7
            psubw mm0, mm2
            psubw mm1, mm3
            psllw mm2, 8
            psllw mm3, 8
            pmullw mm0, mm5
            pmullw mm1, mm5
            paddw mm0, mm2
            paddw mm1, mm3
            psrlw mm0, 8
            psrlw mm1, 8
            lea eax, [edx+ebx]
            psubw mm0, mm1
            shl eax, 16
            psllw mm1, 8
            shr eax, 24
            pmullw mm0, mm6
            movd mm5, eax 
            paddw mm0, mm1
            lea eax, [edx+ebx]
            psrlw mm0, 8
            lea edx, [edx+ebx*2]
            shr eax, 16
            pop ebx
            packuswb mm0, mm7
            movq mm4, mm0
            movd mm0, dword ptr [ecx+eax*4+4]
            movd mm2, dword ptr [ecx+eax*4]
            punpcklwd mm5, mm5
            movd mm1, dword ptr [esi+eax*4+4]
            movd mm3, dword ptr [esi+eax*4]
            punpckldq mm5, mm5
            punpcklbw mm0, mm7
            punpcklbw mm1, mm7
            punpcklbw mm2, mm7
            punpcklbw mm3, mm7
            psubw mm0, mm2
            psubw mm1, mm3
            psllw mm2, 8
            psllw mm3,8
            pmullw mm0, mm5
            pmullw mm1, mm5
            paddw mm0, mm2
            paddw mm1, mm3
            psrlw mm0, 8
            psrlw mm1, 8
            psubw mm0, mm1
            psllw mm1, 8
            pmullw mm0, mm6
            paddw mm0, mm1
            psrlw mm0, 8
            packuswb mm0, mm7
            punpckldq mm4, mm0
            movq qword ptr [edi+ebx*4], mm4
            add ebx, 2
            jnz STARTLOOP
ENDLOOP:
            pop ebx
            and ebx, 1
            je END
            lea edi, [edi+ebx*4]
            neg ebx
            mov eax, edx
            shl eax, 16
            shr eax, 24
            movd mm5, eax
            mov eax, edx
            shr eax, 16
            movd mm2, dword ptr [ecx+eax*4]
            movd mm0, dword ptr [ecx+eax*4+4]
            punpcklwd mm5, mm5
            movd mm3, dword ptr [esi+eax*4]
            movd mm1, dword ptr [esi+eax*4+4]
            punpckldq mm5, mm5
            punpcklbw mm0, mm7
            punpcklbw mm1, mm7
            punpcklbw mm2, mm7
            punpcklbw mm3, mm7
            psubw mm0, mm2
            psubw mm1, mm3
            psllw mm2, 8
            psllw mm3, 8
            pmullw mm0, mm5
            pmullw mm1, mm5
            paddw mm0, mm2
            paddw mm1, mm3
            psrlw mm0, 8
            psrlw mm1, 8
            psubw mm0, mm1
            psllw mm1, 8
            pmullw mm0, mm6
            paddw mm0, mm1
            psrlw mm0, 8
            packuswb mm0, mm7
            movd dword ptr [edi+ebx*4], mm0
END:
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += dstByteWidth;
    }
    __asm
    {
        emms
    }

    return TRUE;
}

inline void BicubicPixel(const PBITMAPEX lpbBitmap, const long x16, const long y16, LPXARGB result)
{
    DWORD u8 = (BYTE)(x16 >> 8);
    DWORD v8 = (BYTE)(y16 >> 8);
    const LPXARGB pixel = (LPXARGB)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * ((y16 >> 16) - 1)) + ((x16 >> 16) - 1);

    long au8[4], av8[4];
    au8[0] = StretchTable[(1 << 8) + u8];
    au8[1] = StretchTable[u8];
    au8[2] = StretchTable[(1 << 8) - u8];
    au8[3] = StretchTable[(2 << 8) - u8];
    av8[0] = StretchTable[(1 << 8) + v8];
    av8[1] = StretchTable[v8];
    av8[2] = StretchTable[(1 << 8) - v8];
    av8[3] = StretchTable[(2 << 8) - v8];

    long sR = 0, sG = 0, sB = 0, sA = 0;
    for (long i = 0; i < 4; ++i)
    {
        long aA = au8[0] * pixel[0].alpha + au8[1] * pixel[1].alpha + au8[2] * pixel[2].alpha + au8[3] * pixel[3].alpha;
        long aR = au8[0] * pixel[0].red + au8[1] * pixel[1].red + au8[2] * pixel[2].red + au8[3] * pixel[3].red;
        long aG = au8[0] * pixel[0].green + au8[1] * pixel[1].green + au8[2] * pixel[2].green + au8[3] * pixel[3].green;
        long aB = au8[0] * pixel[0].blue + au8[1] * pixel[1].blue + au8[2] * pixel[2].blue + au8[3] * pixel[3].blue;
        sA += aA * av8[i];
        sR += aR * av8[i];
        sG += aG * av8[i];
        sB += aB * av8[i];
        ((BYTE*&)pixel) += lpbBitmap->bmWidthBytes;
    }

    result->alpha = ColorTable[sA>>16];
    result->red = ColorTable[sR>>16];
    result->green = ColorTable[sG>>16];
    result->blue = ColorTable[sB>>16];
}

inline void BicubicBorder(const PBITMAPEX lpbBitmap, const long x16, const long y16, LPXARGB result)
{
    long x0Sub = (x16 >> 16) - 1;
    long y0Sub = (y16 >> 16) - 1;
    DWORD u16 = ((WORD)(x16)) + (1 << 16);
    DWORD v16 = ((WORD)(y16)) + (1 << 16);

    xARGB pixel[16];
    long i;

    for (i = 0; i < 4; ++i)
    {
        long y = min(max(y0Sub + i, 0), lpbBitmap->bmHeight - 1);
        long x0 = min(max(x0Sub + 0, 0), lpbBitmap->bmWidth - 1);
        long x1 = min(max(x0Sub + 1, 0), lpbBitmap->bmWidth - 1);
        long x2 = min(max(x0Sub + 2, 0), lpbBitmap->bmWidth - 1);
        long x3 = min(max(x0Sub + 3, 0), lpbBitmap->bmWidth - 1);
        pixel[i*4+0] =* ((LPXARGB)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y) + x0);
        pixel[i*4+1] =* ((LPXARGB)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y) + x1);
        pixel[i*4+2] =* ((LPXARGB)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y) + x2);
        pixel[i*4+3] =* ((LPXARGB)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y) + x3);
    }

    BITMAPEX bmp;
    bmp.bmBits = &pixel[0];
    bmp.bmWidthBytes = 4 * sizeof(xARGB);

    BicubicPixel(&bmp, u16, v16, result);
}

BOOL xRender::StretchBltBicubic(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
//    CStretchTable::Instance();
//    CColorTable::Instance();
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth + 1;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight + 1;

    const long csX = -(1 << 15) + ((long)xrIntFloat16 >> 1);
    const long csY = -(1 << 15) + ((long)yrIntFloat16 >> 1);

    long dstWidth = lpbDst->bmWidth;

    long borderY1 = ((1 << 16) - csY) / (long)yrIntFloat16 + 1;
    if (borderY1 >= lpbDst->bmHeight)
        borderY1 = lpbDst->bmHeight;
    long borderX1 = ((1 << 16) - csX) / (long)xrIntFloat16 + 1;
    if (borderX1 >= lpbDst->bmWidth)
        borderX1 = lpbDst->bmWidth;
    long borderY2 = (((lpbSrc->bmHeight - 3) << 16) - csY) / (long)yrIntFloat16 + 1;
    if (borderY2 < borderY1)
        borderY2 = borderY1;
    long borderX2 = (((lpbSrc->bmWidth - 3) << 16) - csX) / (long)xrIntFloat16 + 1; 
    if (borderX2 < borderX1)
        borderX2 = borderX1;

    LPXARGB pDstLine = (LPXARGB)lpbDst->bmBits;
    long srcy16 = csY;
    long y;
    for (y = 0; y < borderY1; ++y)
    {
        long srcx16 = csX;
        for (long x = 0; x < dstWidth; ++x)
        {
            BicubicBorder(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    for (y = borderY1; y < borderY2; ++y)
    {
        long srcx16 = csX;
        long x;
        for (x = 0; x < borderX1; ++x)
        {
            BicubicBorder(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        for (x = borderX1; x < borderX2; ++x)
        {
            BicubicPixel(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        for (x = borderX2; x < dstWidth; ++x)
        {
            BicubicBorder(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    for (y = borderY2; y < lpbDst->bmHeight; ++y)
    {
        long srcx16 = csX;
        for (long x = 0; x < dstWidth; ++x)
        {
            BicubicBorder(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    return TRUE;
}

inline void BicubicPixelMMX(const PBITMAPEX lpbBitmap, const long x16, const long y16, LPXARGB result)
{
    __asm
    {
        mov     ecx, lpbBitmap
        mov     eax, y16
        mov     ebx, x16
        movzx   edi, ah
        mov     edx, [ecx]BITMAP.bmBits
        shr     eax, 16
        mov     esi, [ecx]BITMAP.bmWidthBytes
        dec     eax
        movzx   ecx, bh
        shr     ebx, 16
        imul    eax, esi
        lea     edx, [edx+ebx*4-4]
        add     edx, eax

        mov     eax, ecx
        neg     ecx

        pxor    mm7, mm7
        pxor    mm0, mm0

        lea     ebx, [StretchTable+256*4+edi*4]

        movd        mm1, dword ptr [edx]
        movd        mm2, dword ptr [edx+4]
        movd        mm3, dword ptr [edx+8]
        movd        mm4, dword ptr [edx+12]
        movd        mm5, dword ptr [StretchTable+256*4+eax*4]
        movd        mm6, dword ptr [StretchTable+eax*4]
        punpcklbw   mm1, mm7
        punpcklbw   mm2, mm7
        punpcklwd   mm5, mm5
        punpcklwd   mm6, mm6
        psllw       mm1, 7
        psllw       mm2, 7
        pmulhw      mm1, mm5
        pmulhw      mm2, mm6
        punpcklbw   mm3, mm7
        punpcklbw   mm4, mm7
        movd        mm5, dword ptr [StretchTable+256*4+ecx*4]
        movd        mm6, dword ptr [StretchTable+512*4+ecx*4]
        punpcklwd   mm5, mm5
        punpcklwd   mm6 ,mm6
        psllw       mm3, 7
        psllw       mm4, 7
        pmulhw      mm3, mm5
        pmulhw      mm4, mm6
        paddsw      mm1, mm2
        paddsw      mm3, mm4
        movd        mm6, dword ptr [ebx]
        paddsw      mm1, mm3
        punpcklwd   mm6, mm6

        pmulhw      mm1, mm6
        add         edx, esi
        paddsw      mm0, mm1

        lea         ebx, [StretchTable+edi*4]

        movd        mm1, dword ptr [edx]
        movd        mm2, dword ptr [edx+4]
        movd        mm3, dword ptr [edx+8]
        movd        mm4, dword ptr [edx+12]
        movd        mm5, dword ptr [StretchTable+256*4+eax*4]
        movd        mm6, dword ptr [StretchTable+eax*4]
        punpcklbw   mm1, mm7
        punpcklbw   mm2, mm7
        punpcklwd   mm5, mm5
        punpcklwd   mm6, mm6
        psllw       mm1, 7
        psllw       mm2, 7
        pmulhw      mm1, mm5
        pmulhw      mm2, mm6
        punpcklbw   mm3, mm7
        punpcklbw   mm4, mm7
        movd        mm5, dword ptr [StretchTable+256*4+ecx*4]
        movd        mm6, dword ptr [StretchTable+512*4+ecx*4]
        punpcklwd   mm5, mm5
        punpcklwd   mm6 ,mm6
        psllw       mm3, 7
        psllw       mm4, 7
        pmulhw      mm3, mm5
        pmulhw      mm4, mm6
        paddsw      mm1, mm2
        paddsw      mm3, mm4
        movd        mm6, dword ptr [ebx]
        paddsw      mm1, mm3
        punpcklwd   mm6, mm6

        pmulhw      mm1, mm6
        add         edx, esi
        paddsw      mm0, mm1

        neg    edi
        lea    ebx, [StretchTable+256*4+edi*4]

        movd        mm1, dword ptr [edx]
        movd        mm2, dword ptr [edx+4]
        movd        mm3, dword ptr [edx+8]
        movd        mm4, dword ptr [edx+12]
        movd        mm5, dword ptr [StretchTable+256*4+eax*4]
        movd        mm6, dword ptr [StretchTable+eax*4]
        punpcklbw   mm1, mm7
        punpcklbw   mm2, mm7
        punpcklwd   mm5, mm5
        punpcklwd   mm6, mm6
        psllw       mm1, 7
        psllw       mm2, 7
        pmulhw      mm1, mm5
        pmulhw      mm2, mm6
        punpcklbw   mm3, mm7
        punpcklbw   mm4, mm7
        movd        mm5, dword ptr [StretchTable+256*4+ecx*4]
        movd        mm6, dword ptr [StretchTable+512*4+ecx*4]
        punpcklwd   mm5, mm5
        punpcklwd   mm6 ,mm6
        psllw       mm3, 7
        psllw       mm4, 7
        pmulhw      mm3, mm5
        pmulhw      mm4, mm6
        paddsw      mm1, mm2
        paddsw      mm3, mm4
        movd        mm6, dword ptr [ebx]
        paddsw      mm1, mm3
        punpcklwd   mm6, mm6

        pmulhw      mm1, mm6
        add         edx, esi
        paddsw      mm0, mm1

        lea         ebx, [StretchTable+512*4+edi*4]

        movd        mm1, dword ptr [edx]
        movd        mm2, dword ptr [edx+4]
        movd        mm3, dword ptr [edx+8]
        movd        mm4, dword ptr [edx+12]
        movd        mm5, dword ptr [StretchTable+256*4+eax*4]
        movd        mm6, dword ptr [StretchTable+eax*4]
        punpcklbw   mm1, mm7
        punpcklbw   mm2, mm7
        punpcklwd   mm5, mm5
        punpcklwd   mm6, mm6
        psllw       mm1, 7
        psllw       mm2, 7
        pmulhw      mm1, mm5
        pmulhw      mm2, mm6
        punpcklbw   mm3, mm7
        punpcklbw   mm4, mm7
        movd        mm5, dword ptr [StretchTable+256*4+ecx*4]
        movd        mm6, dword ptr [StretchTable+512*4+ecx*4]
        punpcklwd   mm5, mm5
        punpcklwd   mm6 ,mm6
        psllw       mm3, 7
        psllw       mm4, 7
        pmulhw      mm3, mm5
        pmulhw      mm4, mm6
        paddsw      mm1, mm2
        paddsw      mm3, mm4
        movd        mm6, dword ptr [ebx]
        paddsw      mm1, mm3
        punpcklwd   mm6, mm6

        pmulhw      mm1, mm6
        add         edx, esi
        paddsw      mm0, mm1

        psraw       mm0, 3
        mov         eax, result
        packuswb    mm0, mm7
        movd        [eax], mm0
    }
}

inline void BicubicBorderMMX(const PBITMAPEX lpbBitmap, const long x16, const long y16, LPXARGB result)
{
    long x0Sub = (x16 >> 16) - 1;
    long y0Sub = (y16 >> 16) - 1;
    DWORD u16 = ((WORD)(x16)) + (1 << 16);
    DWORD v16 = ((WORD)(y16)) + (1 << 16);

    xARGB pixel[16];
    long i;

    for (i = 0; i < 4; ++i)
    {
        long y = min(max(y0Sub + i, 0), lpbBitmap->bmHeight - 1);
        long x0 = min(max(x0Sub + 0, 0), lpbBitmap->bmWidth - 1);
        long x1 = min(max(x0Sub + 1, 0), lpbBitmap->bmWidth - 1);
        long x2 = min(max(x0Sub + 2, 0), lpbBitmap->bmWidth - 1);
        long x3 = min(max(x0Sub + 3, 0), lpbBitmap->bmWidth - 1);
        pixel[i*4+0] =* ((LPXARGB)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y) + x0);
        pixel[i*4+1] =* ((LPXARGB)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y) + x1);
        pixel[i*4+2] =* ((LPXARGB)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y) + x2);
        pixel[i*4+3] =* ((LPXARGB)((LPBYTE)lpbBitmap->bmBits + lpbBitmap->bmWidthBytes * y) + x3);
    }

    BITMAPEX bmp;
    bmp.bmBits = &pixel[0];
    bmp.bmWidthBytes = 4 * sizeof(xARGB);

    BicubicPixelMMX(&bmp, u16, v16, result);
}

BOOL xRender::StretchBltBicubicMMX(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
//    CStretchTable::Instance();
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth + 1;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight + 1;

    const long csX = -(1 << 15) + ((long)xrIntFloat16 >> 1);
    const long csY = -(1 << 15) + ((long)yrIntFloat16 >> 1);

    long dstWidth = lpbDst->bmWidth;

    long borderY1 = ((1 << 16) - csY) / (long)yrIntFloat16 + 1;
    if (borderY1 >= lpbDst->bmHeight)
        borderY1 = lpbDst->bmHeight;
    long borderX1 = ((1 << 16) - csX) / (long)xrIntFloat16 + 1;
    if (borderX1 >= lpbDst->bmWidth)
        borderX1 = lpbDst->bmWidth;
    long borderY2 = (((lpbSrc->bmHeight - 3) << 16) - csY) / (long)yrIntFloat16 + 1;
    if (borderY2 < borderY1)
        borderY2 = borderY1;
    long borderX2 = (((lpbSrc->bmWidth - 3) << 16) - csX) / (long)xrIntFloat16 + 1; 
    if (borderX2 < borderX1)
        borderX2 = borderX1;

    LPXARGB pDstLine = (LPXARGB)lpbDst->bmBits;
    long srcy16 = csY;
    long y;
    for (y = 0; y < borderY1; ++y)
    {
        long srcx16 = csX;
        for (long x = 0; x < dstWidth; ++x)
        {
            BicubicBorderMMX(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    for (y = borderY1; y < borderY2; ++y)
    {
        long srcx16 = csX;
        long x;
        for (x = 0; x < borderX1; ++x)
        {
            BicubicBorderMMX(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        for (x = borderX1; x < borderX2; ++x)
        {
            BicubicPixelMMX(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        for (x = borderX2; x < dstWidth; ++x)
        {
            BicubicBorderMMX(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    for (y = borderY2; y < lpbDst->bmHeight; ++y)
    {
        long srcx16 = csX;
        for (long x = 0; x < dstWidth; ++x)
        {
            BicubicBorderMMX(lpbSrc, srcx16, srcy16, &pDstLine[x]);
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        ((BYTE*&)pDstLine) += lpbDst->bmWidthBytes;
    }
    __asm
    {
        emms
    }
    return TRUE;
}

BOOL xRender::StretchBltBicubic(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
//    CStretchTable::Instance();
//    CColorTable::Instance();
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx + 1;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy + 1;

    const long csX = -(1 << 15) + ((long)xrIntFloat16 >> 1);
    const long csY = -(1 << 15) + ((long)yrIntFloat16 >> 1);

    long borderY1 = ((1 << 16) - csY) / (long)yrIntFloat16 + 1;
    if (borderY1 >= lpszDst->cy)
        borderY1 = lpszDst->cy;
    long borderX1 = ((1 << 16) - csX) / (long)xrIntFloat16 + 1;
    if (borderX1 >= lpszDst->cx)
        borderX1 = lpszDst->cx;
    long borderY2 = (((lpbSrc->bmHeight - 3) << 16) - csY) / (long)yrIntFloat16 + 1;
    if (borderY2 < borderY1)
        borderY2 = borderY1;
    long borderX2 = (((lpbSrc->bmWidth - 3) << 16) - csX) / (long)xrIntFloat16 + 1; 
    if (borderX2 < borderX1)
        borderX2 = borderX1;

    DWORD dstByteWidth = lpbDst->bmWidthBytes;
    LPXARGB pDstLine = (LPXARGB)lpbDst->bmBits;
    long srcy16 = csY + lprcClipped->top * yrIntFloat16;
    long srcxOriginal16 = csX + lprcClipped->left * xrIntFloat16;

    if (borderX1 > lprcClipped->right) borderX1 = lprcClipped->right;
    if (borderX2 > lprcClipped->right) borderX2 = lprcClipped->right;
    if (borderY1 > lprcClipped->bottom) borderY1 = lprcClipped->bottom;
    if (borderY2 > lprcClipped->bottom) borderY2 = lprcClipped->bottom;
    if (borderX1 < lprcClipped->left) borderX1 = lprcClipped->left;
    if (borderX2 < lprcClipped->left) borderX2 = lprcClipped->left;
    if (borderY1 < lprcClipped->top) borderY1 = lprcClipped->top;
    if (borderY2 < lprcClipped->top) borderY2 = lprcClipped->top;

    long y;
    for (y = lprcClipped->top; y < borderY1; ++y)
    {
        long srcx16 = srcxOriginal16;

        for (long x = lprcClipped->left; x < lprcClipped->right; ++x)
        {
            BicubicBorder(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }

    for (y = borderY1; y < borderY2; ++y)
    {
        long srcx16 = srcxOriginal16;
        long x;

        for (x = lprcClipped->left; x < borderX1; ++x)
        {
            BicubicBorder(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        for (x = borderX1; x < borderX2; ++x)
        {
            BicubicPixel(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        for (x = borderX2; x < lprcClipped->right; ++x)
        {
            BicubicBorder(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }

    for (y = borderY2; y < lprcClipped->bottom; ++y)
    {
        long srcx16 = srcxOriginal16;

        for (long x = lprcClipped->left; x < lprcClipped->right; ++x)
        {
            BicubicBorder(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }

    return TRUE;
}

BOOL xRender::StretchBltBicubicMMX(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
//    CStretchTable::Instance();
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx + 1;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy + 1;

    const long csX = -(1 << 15) + ((long)xrIntFloat16 >> 1);
    const long csY = -(1 << 15) + ((long)yrIntFloat16 >> 1);

    long borderY1 = ((1 << 16) - csY) / (long)yrIntFloat16 + 1;
    if (borderY1 >= lpszDst->cy)
        borderY1 = lpszDst->cy;
    long borderX1 = ((1 << 16) - csX) / (long)xrIntFloat16 + 1;
    if (borderX1 >= lpszDst->cx)
        borderX1 = lpszDst->cx;
    long borderY2 = (((lpbSrc->bmHeight - 3) << 16) - csY) / (long)yrIntFloat16 + 1;
    if (borderY2 < borderY1)
        borderY2 = borderY1;
    long borderX2 = (((lpbSrc->bmWidth - 3) << 16) - csX) / (long)xrIntFloat16 + 1; 
    if (borderX2 < borderX1)
        borderX2 = borderX1;

    LPXARGB pDstLine = (LPXARGB)lpbDst->bmBits;
    DWORD dstByteWidth = lpbDst->bmWidthBytes;
    long srcy16 = csY + lprcClipped->top * yrIntFloat16;
    long srcxOriginal16 = csX + lprcClipped->left * xrIntFloat16;

    if (borderX1 > lprcClipped->right) borderX1 = lprcClipped->right;
    if (borderX2 > lprcClipped->right) borderX2 = lprcClipped->right;
    if (borderY1 > lprcClipped->bottom) borderY1 = lprcClipped->bottom;
    if (borderY2 > lprcClipped->bottom) borderY2 = lprcClipped->bottom;
    if (borderX1 < lprcClipped->left) borderX1 = lprcClipped->left;
    if (borderX2 < lprcClipped->left) borderX2 = lprcClipped->left;
    if (borderY1 < lprcClipped->top) borderY1 = lprcClipped->top;
    if (borderY2 < lprcClipped->top) borderY2 = lprcClipped->top;

    long y;
    for (y = lprcClipped->top; y < borderY1; ++y)
    {
        long srcx16 = srcxOriginal16;

        for (long x = lprcClipped->left; x < lprcClipped->right; ++x)
        {
            BicubicBorderMMX(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }

    for (y = borderY1; y < borderY2; ++y)
    {
        long srcx16 = srcxOriginal16;
        long x;

        for (x = lprcClipped->left; x < borderX1; ++x)
        {
            BicubicBorderMMX(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        for (x = borderX1; x < borderX2; ++x)
        {
            BicubicPixelMMX(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        for (x = borderX2; x < lprcClipped->right; ++x)
        {
            BicubicBorderMMX(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }

    for (y = borderY2; y < lprcClipped->bottom; ++y)
    {
        long srcx16 = srcxOriginal16;

        for (long x = lprcClipped->left; x < lprcClipped->right; ++x)
        {
            BicubicBorderMMX(lpbSrc, srcx16, srcy16, &pDstLine[x - lprcClipped->left]);
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        ((LPBYTE&)pDstLine) += dstByteWidth;
    }

    __asm
    {
        emms
    }
    return TRUE;
}

BOOL xRender::StretchBlt(PBITMAP lpbDst, PBITMAPEX lpbSrc, xARGB xKey)
{
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;

    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight;
    DWORD dstWidth = lpbDst->bmWidth;
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD srcy16 = 0;

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        DWORD srcx16 = 0;
        for (DWORD x = 0; x < dstWidth; ++x)
        {
            DWORD c = lpScanSrc[srcx16 >> 16];
            if (c != xKey.argb)
            {
                lpScanDst[x] = c;
            }
            srcx16 += xrIntFloat16;
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    return TRUE;
}

BOOL xRender::StretchBltSSE(PBITMAP lpbDst, PBITMAPEX lpbSrc, xARGB xKey)
{
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;

    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight;
    DWORD dstWidth = lpbDst->bmWidth;
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD srcy16 = 0;

    DWORD keyColor = xKey.argb;
    __asm
    {
        movd mm2, keyColor
        punpckldq mm2, mm2
    }

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + srcByteWidth * (srcy16 >> 16));
        __asm
        {
            mov esi, lpScanSrc
            mov edi, lpScanDst
            mov edx, xrIntFloat16
            mov ecx, dstWidth
            xor eax, eax
            and ecx, (not 3)
            je ENDLOOP
            lea edi, [edi+ecx*4]
            neg ecx
WRITELOOP:
            prefetchnta [edi+ecx*4+512]
            movq mm5, [edi+ecx*4]
            movq mm6, [edi+ecx*4+8]
            mov ebx, eax
            shr eax, 16
            movd mm0, [esi+eax*4]
            mov eax, ebx
            lea ebx, [eax+edx]
            shr ebx, 16
            punpckldq mm0, [esi+ebx*4]
            lea eax, [eax+edx*2]
            mov ebx, eax
            shr eax, 16
            movd mm1, [esi+eax*4]
            mov eax, ebx
            lea ebx, [eax+edx]    
            shr ebx, 16
            punpckldq mm1, [esi+ebx*4]
            lea eax, [eax+edx*2]
            movq mm3, mm0
            movq mm4, mm1
            pcmpeqd mm0, mm2
            pcmpeqd mm1, mm2
            psubusb mm3, mm0
            psubusb mm4, mm1
            pand mm0, mm5
            pand mm1, mm6
            por mm0, mm3
            por mm1, mm4
            movq qword ptr [edi+ecx*4], mm0
            movq qword ptr [edi+ecx*4+8], mm1
            add ecx, 4
            jnz WRITELOOP
ENDLOOP:
            mov ebx, eax
            mov ecx, dstWidth
            and ecx, 3
            je END
            lea edi, [edi+ecx*4]
            neg ecx
BORDER:
            mov eax, ebx
            shr eax, 16
            movd mm0, [esi+eax*4]
            movd mm5, [edi+ecx*4]
            movq mm3, mm0
            pcmpeqd mm0, mm2
            psubusb mm3, mm0
            pand mm0, mm5
            por mm0, mm3
            movd [edi+ecx*4], mm0
            add ebx, edx
            inc ecx
            jnz BORDER
END:
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }
    __asm
    {
        emms
    }
    return TRUE;
}

BOOL xRender::StretchBlt(PBITMAP lpbDst, PBITMAPEX lpbSrc, xARGB xKey, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy;

    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;
    DWORD srcy16 = (DWORD)(lprcClipped->top * yrIntFloat16);

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + (srcy16 >> 16) * lpbSrc->bmWidthBytes);
        DWORD srcx16 = (DWORD)(lprcClipped->left * xrIntFloat16);

        for (int x = 0; x < lpbDst->bmWidth; x++)
        {
            DWORD c = lpScanSrc[srcx16 >> 16];
            if (c != xKey.argb)
            {
                lpScanDst[x] = c;
            }
            srcx16 += xrIntFloat16;
        }

        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += lpbDst->bmWidthBytes;
    }

    return TRUE;
}

BOOL xRender::StretchBltSSE(PBITMAP lpbDst, PBITMAPEX lpbSrc, xARGB xKey, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy;

    DWORD dstWidth = lpbDst->bmWidth;
    DWORD srcByteWidth = lpbSrc->bmWidthBytes;
    DWORD dstByteWidth = lpbDst->bmWidthBytes;
    LPDWORD lpScanDst = (LPDWORD)lpbDst->bmBits;

    DWORD srcy16 = (DWORD)((lprcClipped->top) * yrIntFloat16);
    DWORD srcx16 = (DWORD)((lprcClipped->left) * xrIntFloat16);

    DWORD keyColor = xKey.argb;
    __asm
    {
        movd mm2, keyColor
        punpckldq mm2, mm2
    }

    for (int y = 0; y < lpbDst->bmHeight; ++y)
    {
        LPDWORD lpScanSrc = (LPDWORD)((LPBYTE)lpbSrc->bmBits + (srcy16 >> 16) * srcByteWidth);
        DWORD srcIndex = srcx16;

        __asm
        {
            mov esi, lpScanSrc
            mov edi, lpScanDst
            mov edx, xrIntFloat16
            mov ecx, dstWidth
            mov eax, srcIndex
            and ecx, (not 3)
            je ENDLOOP
            lea edi, [edi+ecx*4]
            neg ecx
WRITELOOP:
            prefetchnta [edi+ecx*4+512]
            movq mm5, [edi+ecx*4]
            movq mm6, [edi+ecx*4+8]
            mov ebx, eax
            shr eax, 16
            movd mm0, [esi+eax*4]
            mov eax, ebx
            lea ebx, [eax+edx]
            shr ebx, 16
            punpckldq mm0, [esi+ebx*4]
            lea eax, [eax+edx*2]
            mov ebx, eax
            shr eax, 16
            movd mm1, [esi+eax*4]
            mov eax, ebx
            lea ebx, [eax+edx]    
            shr ebx, 16
            punpckldq mm1, [esi+ebx*4]
            lea eax, [eax+edx*2]
            movq mm3, mm0
            movq mm4, mm1
            pcmpeqd mm0, mm2
            pcmpeqd mm1, mm2
            psubusb mm3, mm0
            psubusb mm4, mm1
            pand mm0, mm5
            pand mm1, mm6
            por mm0, mm3
            por mm1, mm4
            movq qword ptr [edi+ecx*4], mm0
            movq qword ptr [edi+ecx*4+8], mm1
            add ecx, 4
            jnz WRITELOOP
ENDLOOP:
            mov ebx, eax
            mov ecx, dstWidth
            and ecx, 3
            je END
            lea edi, [edi+ecx*4]
            neg ecx
BORDER:
            mov eax, ebx
            shr eax, 16
            movd mm0, [esi+eax*4]
            movd mm5, [edi+ecx*4]
            movq mm3, mm0
            pcmpeqd mm0, mm2
            psubusb mm3, mm0
            pand mm0, mm5
            por mm0, mm3
            movd [edi+ecx*4], mm0
            add ebx, edx
            inc ecx
            jnz BORDER
END:
        }
        srcy16 += yrIntFloat16;
        (LPBYTE&)lpScanDst += dstByteWidth;
    }
    __asm
    {
        emms
    }

    return TRUE;
}

__inline void AddColorValue64(const LPXARGB pBitSrc, DWORD surf, UINT64 &b, UINT64 &g, UINT64 &r, UINT64 &a, UINT64 &s)
{
    b += pBitSrc->blue * surf;
    g += pBitSrc->green * surf;
    r += pBitSrc->red * surf;
    a += pBitSrc->alpha * surf;
    s += surf;
}

__inline void AddColorValue32(const LPXARGB pBitSrc, DWORD surf, DWORD &b, DWORD &g, DWORD &r, DWORD &a, DWORD &s)
{
    b += pBitSrc->blue * surf;
    g += pBitSrc->green * surf;
    r += pBitSrc->red * surf;
    a += pBitSrc->alpha * surf;
    s += surf;
}

void AreaAveraging64(const int left, const int right, const int top, const int bottom, const DWORD xrIntFloat16, const DWORD yrIntFloat16, PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    BYTE* pBitsSrc = (BYTE*)lpbSrc->bmBits;
    DWORD nWidthBytesSrc = lpbSrc->bmWidthBytes;

    #pragma omp parallel for if (bottom - top > 64)
    for (int y = top; y < bottom; y++)
    {
        LPXARGB pBitsDst = (LPXARGB)((LPBYTE)lpbDst->bmBits + (y - top) * lpbDst->bmWidthBytes);
        int dY = y * yrIntFloat16 + (yrIntFloat16 >> 1) - 0x8000;
        int t = dY - (yrIntFloat16 >> 1);
        int b = dY + (yrIntFloat16 >> 1);
        int yi1 = (t + 0x8000) >> 16;
        int yi2 = (b + 0x8000) >> 16;
        DWORD h1 = min((yi1 << 16) + 0x8000, b) - max((yi1 << 16) - 0x8000, t);
        DWORD h2 = min((yi2 << 16) + 0x8000, b) - max((yi2 << 16) - 0x8000, t);
        yi1 = max(yi1, 0);
        yi2 = min(yi2, lpbSrc->bmHeight - 1);

        for (int x = left; x < right; x++)
        {
            int dX = x * xrIntFloat16 + (xrIntFloat16 >> 1) - 0x8000;
            int l = dX - (xrIntFloat16 >> 1);
            int r = dX + (xrIntFloat16 >> 1);
            int xi1 = (l + 0x8000) >> 16;
            int xi2 = (r + 0x8000) >> 16;

            DWORD w1 = min((xi1 << 16) + 0x8000, r) - max((xi1 << 16) - 0x8000, l);
            DWORD w2 = min((xi2 << 16) + 0x8000, r) - max((xi2 << 16) - 0x8000, l);
            xi1 = max(xi1, 0);
            xi2 = min(xi2, lpbSrc->bmWidth - 1);

            UINT64 rr, gg, bb, aa;
            rr = gg = bb = aa = 0;
            UINT64 s = 0;

            LPXARGB pBits;
            pBits = (LPXARGB)(pBitsSrc + yi1 * nWidthBytesSrc);
            AddColorValue64(&pBits[xi1], (UINT64)w1 * (UINT64)h1 >> 16, bb, gg, rr, aa, s);
            AddColorValue64(&pBits[xi2], (UINT64)w2 * (UINT64)h1 >> 16, bb, gg, rr, aa, s);
            pBits = (LPXARGB)(pBitsSrc + yi2 * nWidthBytesSrc);
            AddColorValue64(&pBits[xi1], (UINT64)w1 * (UINT64)h2 >> 16, bb, gg, rr, aa, s);
            AddColorValue64(&pBits[xi2], (UINT64)w2 * (UINT64)h2 >> 16, bb, gg, rr, aa, s);

            for (int j = xi1 + 1; j < xi2; j++)
            {
                pBits = (LPXARGB)(pBitsSrc + yi1 * nWidthBytesSrc);
                AddColorValue64(&pBits[j], h1, bb, gg, rr, aa, s);
                pBits = (LPXARGB)(pBitsSrc + yi2 * nWidthBytesSrc);
                AddColorValue64(&pBits[j], h2, bb, gg, rr, aa, s);
            }

            for (int i = yi1 + 1; i < yi2; i++)
            {
                pBits = (LPXARGB)(pBitsSrc + i * nWidthBytesSrc);
                AddColorValue64(&pBits[xi1], w1, bb, gg, rr, aa, s);
                AddColorValue64(&pBits[xi2], w2, bb, gg, rr, aa, s);
                for (int j = xi1 + 1; j < xi2; j++)
                { 
                    bb += (pBits[j].blue << 16);
                    gg += (pBits[j].green << 16);
                    rr += (pBits[j].red << 16);
                    aa += (pBits[j].alpha << 16);
                    s += (1 << 16);
                }
            }
            pBitsDst[x - left].blue = (BYTE)(bb / s);
            pBitsDst[x - left].green = (BYTE)(gg / s);
            pBitsDst[x - left].red = (BYTE)(rr / s);
            pBitsDst[x - left].alpha = (BYTE)(aa / s);
        }
    }
}

void AreaAveraging32(const int left, const int right, const int top, const int bottom, const DWORD xrIntFloat16, const DWORD yrIntFloat16, PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    BYTE* pBitsSrc = (BYTE*)lpbSrc->bmBits;
    DWORD nWidthBytesSrc = lpbSrc->bmWidthBytes;

    #pragma omp parallel for if (bottom - top > 64)
    for (int y = top; y < bottom; y++)
    {
        LPXARGB pBitsDst = (LPXARGB)((LPBYTE)lpbDst->bmBits + (y - top) * lpbDst->bmWidthBytes);
        int dY = y * yrIntFloat16 + (yrIntFloat16 >> 1) - 0x8000;
        int t = dY - (yrIntFloat16 >> 1);
        int b = dY + (yrIntFloat16 >> 1);
        int yi1 = (t + 0x8000) >> 16;
        int yi2 = (b + 0x8000) >> 16;
        DWORD h1 = min((yi1 << 16) + 0x8000, b) - max((yi1 << 16) - 0x8000, t);
        DWORD h2 = min((yi2 << 16) + 0x8000, b) - max((yi2 << 16) - 0x8000, t);
        yi1 = max(yi1, 0);
        yi2 = min(yi2, lpbSrc->bmHeight - 1);

        for (int x = left; x < right; x++)
        {
            int dX = x * xrIntFloat16 + (xrIntFloat16 >> 1) - 0x8000;
            int l = dX - (xrIntFloat16 >> 1);
            int r = dX + (xrIntFloat16 >> 1);
            int xi1 = (l + 0x8000) >> 16;
            int xi2 = (r + 0x8000) >> 16;

            DWORD w1 = min((xi1 << 16) + 0x8000, r) - max((xi1 << 16) - 0x8000, l);
            DWORD w2 = min((xi2 << 16) + 0x8000, r) - max((xi2 << 16) - 0x8000, l);
            xi1 = max(xi1, 0);
            xi2 = min(xi2, lpbSrc->bmWidth - 1);

            DWORD rr, gg, bb, aa;
            rr = gg = bb = aa = 0;
            DWORD s = 0;

            LPXARGB pBits;
            pBits = (LPXARGB)(pBitsSrc + yi1 * nWidthBytesSrc);
            AddColorValue32(&pBits[xi1], (UINT64)w1 * (UINT64)h1 >> 16, bb, gg, rr, aa, s);
            AddColorValue32(&pBits[xi2], (UINT64)w2 * (UINT64)h1 >> 16, bb, gg, rr, aa, s);
            pBits = (LPXARGB)(pBitsSrc + yi2 * nWidthBytesSrc);
            AddColorValue32(&pBits[xi1], (UINT64)w1 * (UINT64)h2 >> 16, bb, gg, rr, aa, s);
            AddColorValue32(&pBits[xi2], (UINT64)w2 * (UINT64)h2 >> 16, bb, gg, rr, aa, s);

            for (int j = xi1 + 1; j < xi2; j++)
            {
                pBits = (LPXARGB)(pBitsSrc + yi1 * nWidthBytesSrc);
                AddColorValue32(&pBits[j], h1, bb, gg, rr, aa, s);
                pBits = (LPXARGB)(pBitsSrc + yi2 * nWidthBytesSrc);
                AddColorValue32(&pBits[j], h2, bb, gg, rr, aa, s);
            }

            for (int i = yi1 + 1; i < yi2; i++)
            {
                pBits = (LPXARGB)(pBitsSrc + i * nWidthBytesSrc);
                AddColorValue32(&pBits[xi1], w1, bb, gg, rr, aa, s);
                AddColorValue32(&pBits[xi2], w2, bb, gg, rr, aa, s);
                for (int j = xi1 + 1; j < xi2; j++)
                { 
                    bb += (pBits[j].blue << 16);
                    gg += (pBits[j].green << 16);
                    rr += (pBits[j].red << 16);
                    aa += (pBits[j].alpha << 16);
                    s += (1 << 16);
                }
            }
            pBitsDst[x - left].blue = (BYTE)(bb / s);
            pBitsDst[x - left].green = (BYTE)(gg / s);
            pBitsDst[x - left].red = (BYTE)(rr / s);
            pBitsDst[x - left].alpha = (BYTE)(aa / s);
        }
    }
}

BOOL xRender::StretchBltAreaAveraging(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    if (lpbSrc->bmWidth < lpbDst->bmWidth || lpbSrc->bmHeight < lpbDst->bmHeight)
    {
      assert(false);
        // return InterFuncs.pStretchBltBicubic(lpbDst, lpbSrc);
    }
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight;

    if (xrIntFloat16 > 0x100000 || yrIntFloat16 > 0x100000)
    {
        AreaAveraging64(0, lpbDst->bmWidth, 0, lpbDst->bmHeight, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    else
    {
        AreaAveraging32(0, lpbDst->bmWidth, 0, lpbDst->bmHeight, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    return TRUE;
}

BOOL xRender::StretchBltAreaAveraging(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
    if (lpbSrc->bmWidth < lpszDst->cx || lpbSrc->bmHeight < lpszDst->cy)
    {
      assert(false);
        // return InterFuncs.pStretchBltBicubicPart(lpbDst, lpbSrc, lpszDst, lprcClipped);
    }
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy;

    if (xrIntFloat16 > 0x100000 || yrIntFloat16 > 0x100000)
    {
        AreaAveraging64(lprcClipped->left, lprcClipped->right, lprcClipped->top, lprcClipped->bottom, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    else
    {
        AreaAveraging32(lprcClipped->left, lprcClipped->right, lprcClipped->top, lprcClipped->bottom, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    return TRUE;
}

__forceinline void AddColorValue64SSE2(const LPXARGB pBitSrc, DWORD surf, __m64 &b, __m64 &g, __m64 &r, __m64 &a, __m64 &s)
{
    __m64 v0 = _m_from_int(pBitSrc->argb);
    __m64 v1 = _m_psrlqi(_m_from_int(surf), 8);
    __m64 v2 = _mm_setzero_si64();
    s = _mm_add_si64(s, v1);
    v1 = _m_punpcklwd(v1, v1);
    v0 = _m_punpcklbw(v0, v2);
    v1 = _m_punpckldq(v1, v1);
    v0 = _m_pmullw(v0, v1);
    v1 = _m_psrlqi(v0, 48);
    v2 = _m_psllqi(v0, 16);
    v2 = _m_psrlqi(v2, 48);
    a = _mm_add_si64(a, v1);
    r = _mm_add_si64(r, v2);
    v1 = _m_psllqi(v0, 32);
    v2 = _m_psllqi(v0, 48);
    v1 = _m_psrlqi(v1, 48);
    v2 = _m_psrlqi(v2, 48);
    g = _mm_add_si64(g, v1);
    b = _mm_add_si64(b, v2);
}

__forceinline void AddColorValue32SSE2(const LPXARGB pBitSrc, DWORD surf, __m64 &bg, __m64 &ra, DWORD &s)
{
    __m64 v0 = _m_from_int(pBitSrc->argb);
    DWORD sv = surf >> 8;
    __m64 mzero = _mm_setzero_si64();
    s += sv;
    __m64 msurf = _m_from_int(sv);
    msurf = _m_punpcklwd(msurf, msurf);
    v0 = _m_punpcklbw(v0, mzero);
    msurf = _m_punpckldq(msurf, msurf);
    v0 = _m_pmullw(v0, msurf);
    __m64 v1 = v0;
    v0 = _m_punpckhwd(v0, mzero);
    v1 = _m_punpcklwd(v1, mzero);
    ra = _m_paddd(ra, v0);
    bg = _m_paddd(bg, v1);
}

void AreaAveraging64SSE2(const int left, const int right, const int top, const int bottom, const DWORD xrIntFloat16, const DWORD yrIntFloat16, PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    BYTE* pBitsSrc = (BYTE*)lpbSrc->bmBits;
    DWORD nWidthBytesSrc = lpbSrc->bmWidthBytes;

    #pragma omp parallel for if (bottom - top > 64)
    for (int y = top; y < bottom; y++)
    {
        LPXARGB pBitsDst = (LPXARGB)((LPBYTE)lpbDst->bmBits + (y - top) * lpbDst->bmWidthBytes);
        int dY = y * yrIntFloat16 + (yrIntFloat16 >> 1) - 0x8000;
        int t = dY - (yrIntFloat16 >> 1);
        int b = dY + (yrIntFloat16 >> 1);
        int yi1 = (t + 0x8000) >> 16;
        int yi2 = (b + 0x8000) >> 16;
        DWORD h1 = min((yi1 << 16) + 0x8000, b) - max((yi1 << 16) - 0x8000, t);
        DWORD h2 = min((yi2 << 16) + 0x8000, b) - max((yi2 << 16) - 0x8000, t);
        yi1 = max(yi1, 0);
        yi2 = min(yi2, lpbSrc->bmHeight - 1);

        for (int x = left; x < right; x++)
        {
            int dX = x * xrIntFloat16 + (xrIntFloat16 >> 1) - 0x8000;
            int l = dX - (xrIntFloat16 >> 1);
            int r = dX + (xrIntFloat16 >> 1);
            int xi1 = (l + 0x8000) >> 16;
            int xi2 = (r + 0x8000) >> 16;
                
            __m64 ma = _mm_setzero_si64();
            __m64 mr = _mm_setzero_si64();
            __m64 mg = _mm_setzero_si64();
            __m64 mb = _mm_setzero_si64();
            __m64 ms = _mm_setzero_si64();

            DWORD w1 = min((xi1 << 16) + 0x8000, r) - max((xi1 << 16) - 0x8000, l);
            DWORD w2 = min((xi2 << 16) + 0x8000, r) - max((xi2 << 16) - 0x8000, l);
            xi1 = max(xi1, 0);
            xi2 = min(xi2, lpbSrc->bmWidth - 1);

            LPXARGB pBits;
            pBits = (LPXARGB)(pBitsSrc + yi1 * nWidthBytesSrc);
            AddColorValue64SSE2(&pBits[xi1], ((UINT64)w1 * (UINT64)h1 + 0xFFFFFF) >> 16, mb, mg, mr, ma, ms);
            AddColorValue64SSE2(&pBits[xi2], ((UINT64)w2 * (UINT64)h1 + 0xFFFFFF) >> 16, mb, mg, mr, ma, ms);
            pBits = (LPXARGB)(pBitsSrc + yi2 * nWidthBytesSrc);
            AddColorValue64SSE2(&pBits[xi1], ((UINT64)w1 * (UINT64)h2 + 0xFFFFFF) >> 16, mb, mg, mr, ma, ms);
            AddColorValue64SSE2(&pBits[xi2], ((UINT64)w2 * (UINT64)h2 + 0xFFFFFF) >> 16, mb, mg, mr, ma, ms);

            for (int j = xi1 + 1; j < xi2; j++)
            {
                pBits = (LPXARGB)(pBitsSrc + yi1 * nWidthBytesSrc);
                AddColorValue64SSE2(&pBits[j], h1, mb, mg, mr, ma, ms);
                pBits = (LPXARGB)(pBitsSrc + yi2 * nWidthBytesSrc);
                AddColorValue64SSE2(&pBits[j], h2, mb, mg, mr, ma, ms);
            }

            for (int i = yi1 + 1; i < yi2; i++)
            {
                pBits = (LPXARGB)(pBitsSrc + i * nWidthBytesSrc);
                AddColorValue64SSE2(&pBits[xi1], w1, mb, mg, mr, ma, ms);
                AddColorValue64SSE2(&pBits[xi2], w2, mb, mg, mr, ma, ms);

                for (int j = xi1 + 1; j < xi2; j++)
                {
                    __m64 v0 = _m_from_int(pBits[j].argb);
                    __m64 v1 = _m_psllqi(v0, 32);
                    __m64 v2 = _m_psllqi(v0, 40);
                    v1 = _m_psrlqi(v1, 48);
                    v2 = _m_psrlqi(v2, 48);
                    ma = _mm_add_si64(ma, v1);
                    mr = _mm_add_si64(mr, v2);
                    v1 = _m_psllqi(v0, 48);
                    v2 = _m_psllqi(v0, 56);
                    v1 = _m_psrlqi(v1, 48);
                    v2 = _m_psrlqi(v2, 48);
                    mg = _mm_add_si64(mg, v1);
                    mb = _mm_add_si64(mb, v2);
                    ms = _mm_add_si64(ms, _m_from_int(256));
                }
            }

            UINT64 s;
            UINT64 aa, rr, gg, bb;

            aa = ma.m64_u64;
            rr = mr.m64_u64;
            gg = mg.m64_u64;
            bb = mb.m64_u64;
            s = ms.m64_u64;

            pBitsDst[x - left].blue = (BYTE)(bb / s);
            pBitsDst[x - left].green = (BYTE)(gg / s);
            pBitsDst[x - left].red = (BYTE)(rr / s);
            pBitsDst[x - left].alpha = (BYTE)(aa / s);
        }
    }
    _m_empty();
}

void AreaAveraging32SSE2(const int left, const int right, const int top, const int bottom, const DWORD xrIntFloat16, const DWORD yrIntFloat16, PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    BYTE* pBitsSrc = (BYTE*)lpbSrc->bmBits;
    DWORD nWidthBytesSrc = lpbSrc->bmWidthBytes;

    #pragma omp parallel for if (bottom - top > 64)
    for (int y = top; y < bottom; y++)
    {
        LPXARGB pBitsDst = (LPXARGB)((LPBYTE)lpbDst->bmBits + (y - top) * lpbDst->bmWidthBytes);
        int dY = y * yrIntFloat16 + (yrIntFloat16 >> 1) - 0x8000;
        int t = dY - (yrIntFloat16 >> 1);
        int b = dY + (yrIntFloat16 >> 1);
        int yi1 = (t + 0x8000) >> 16;
        int yi2 = (b + 0x8000) >> 16;
        DWORD h1 = min((yi1 << 16) + 0x8000, b) - max((yi1 << 16) - 0x8000, t);
        DWORD h2 = min((yi2 << 16) + 0x8000, b) - max((yi2 << 16) - 0x8000, t);
        yi1 = max(yi1, 0);
        yi2 = min(yi2, lpbSrc->bmHeight - 1);

        for (int x = left; x < right; x++)
        {
            int dX = x * xrIntFloat16 + (xrIntFloat16 >> 1) - 0x8000;
            int l = dX - (xrIntFloat16 >> 1);
            int r = dX + (xrIntFloat16 >> 1);
            int xi1 = (l + 0x8000) >> 16;
            int xi2 = (r + 0x8000) >> 16;

            __m64 ra = _mm_setzero_si64();
            __m64 bg = _mm_setzero_si64();
            DWORD s = 0;

            DWORD w1 = min((xi1 << 16) + 0x8000, r) - max((xi1 << 16) - 0x8000, l);
            DWORD w2 = min((xi2 << 16) + 0x8000, r) - max((xi2 << 16) - 0x8000, l);
            xi1 = max(xi1, 0);
            xi2 = min(xi2, lpbSrc->bmWidth - 1);

            LPXARGB pBits;
            pBits = (LPXARGB)(pBitsSrc + yi1 * nWidthBytesSrc);
            AddColorValue32SSE2(&pBits[xi1], ((UINT64)w1 * (UINT64)h1 + 0xFFFFFF) >> 16, bg, ra, s);
            AddColorValue32SSE2(&pBits[xi2], ((UINT64)w2 * (UINT64)h1 + 0xFFFFFF) >> 16, bg, ra, s);
            pBits = (LPXARGB)(pBitsSrc + yi2 * nWidthBytesSrc);
            AddColorValue32SSE2(&pBits[xi1], ((UINT64)w1 * (UINT64)h2 + 0xFFFFFF) >> 16, bg, ra, s);
            AddColorValue32SSE2(&pBits[xi2], ((UINT64)w2 * (UINT64)h2 + 0xFFFFFF) >> 16, bg, ra, s);

            for (int j = xi1 + 1; j < xi2; j++)
            {
                pBits = (LPXARGB)(pBitsSrc + yi1 * nWidthBytesSrc);
                AddColorValue32SSE2(&pBits[j], h1, bg, ra, s);
                pBits = (LPXARGB)(pBitsSrc + yi2 * nWidthBytesSrc);
                AddColorValue32SSE2(&pBits[j], h2, bg, ra, s);
            }

            for (int i = yi1 + 1; i < yi2; i++)
            {
                pBits = (LPXARGB)(pBitsSrc + i * nWidthBytesSrc);
                AddColorValue32SSE2(&pBits[xi1], w1, bg, ra, s);
                AddColorValue32SSE2(&pBits[xi2], w2, bg, ra, s);

                __m64 mv0 = _mm_setzero_si64();
                __m64 mzero = _mm_setzero_si64();

                for (int j = xi1 + 1; j < xi2; j++)
                {
                    __m64 v = _m_from_int(pBits[j].argb);
                    v = _m_punpcklbw(v, mzero);
                    s += 256;
                    mv0 = _m_paddw(mv0, v);
                }

                __m64 mv1 = mv0;
                mv0 = _m_punpckhwd(mv0, mzero);
                mv1 = _m_punpcklwd(mv1, mzero);
                mv0 = _m_pslldi(mv0, 8);
                mv1 = _m_pslldi(mv1, 8);
                ra = _m_paddd(ra, mv0);
                bg = _m_paddd(bg, mv1);
            }

            DWORD aa, rr, gg, bb;

            aa = ra.m64_u32[1];
            rr = ra.m64_u32[0];
            gg = bg.m64_u32[1];
            bb = bg.m64_u32[0];

            pBitsDst[x - left].blue = (BYTE)(bb / s);
            pBitsDst[x - left].green = (BYTE)(gg / s);
            pBitsDst[x - left].red = (BYTE)(rr / s);
            pBitsDst[x - left].alpha = (BYTE)(aa / s);
        }
    }
    _m_empty();
}

BOOL xRender::StretchBltAreaAveragingSSE2(PBITMAP lpbDst, PBITMAPEX lpbSrc)
{
    if (lpbSrc->bmWidth < lpbDst->bmWidth || lpbSrc->bmHeight < lpbDst->bmHeight)
    {
      assert(false);
        // return InterFuncs.pStretchBltBicubic(lpbDst, lpbSrc);
    }
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpbDst->bmWidth;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpbDst->bmHeight;

    if (xrIntFloat16 > 0x100000 || yrIntFloat16 > 0x100000)
    {
        AreaAveraging64SSE2(0, lpbDst->bmWidth, 0, lpbDst->bmHeight, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    else
    {
        AreaAveraging32SSE2(0, lpbDst->bmWidth, 0, lpbDst->bmHeight, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    return TRUE;
}

BOOL xRender::StretchBltAreaAveragingSSE2(PBITMAP lpbDst, PBITMAPEX lpbSrc, LPCSIZE lpszDst, LPCRECT lprcClipped)
{
    if (lpbSrc->bmWidth < lpszDst->cx || lpbSrc->bmHeight < lpszDst->cy)
    {
      assert(false);
        // return InterFuncs.pStretchBltBicubicPart(lpbDst, lpbSrc, lpszDst, lprcClipped);
    }
    DWORD xrIntFloat16 = (lpbSrc->bmWidth << 16) / lpszDst->cx;
    DWORD yrIntFloat16 = (lpbSrc->bmHeight << 16) / lpszDst->cy;

    if (xrIntFloat16 > 0x100000 || yrIntFloat16 > 0x100000)
    {
        AreaAveraging64SSE2(lprcClipped->left, lprcClipped->right, lprcClipped->top, lprcClipped->bottom, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    else
    {
        AreaAveraging32SSE2(lprcClipped->left, lprcClipped->right, lprcClipped->top, lprcClipped->bottom, xrIntFloat16, yrIntFloat16, lpbDst, lpbSrc);
    }
    return TRUE;
}